﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Space_Conquest
{

    enum ScrollDirection
    {
        Left = 1,
        Right = 2,
        Up = 4,
        Down = 8
    }

    class Map : IRender, IInput, IMotion
    {

        #region Constructors

        public Map()
        {
        }

        public Map(GameEngine parent)
        {

            _parent = parent;

            LoadTextures();

            // Create the Default grass map

            _xTiles = 20;
            _yTiles = 15;

            _tiles = new Tile[_xTiles, _yTiles];
            _groundMap = new Unit[_xTiles, _yTiles];
            _airMap = new Unit[_xTiles, _yTiles];

            for (int j = 0; j < _yTiles; j++)
            {
                for (int i = 0; i < _xTiles; i++)
                {
                    _tiles[i, j] = new Tile(_textures[0]);
                    _tiles[i, j].ID = 0;
                    _groundMap[i, j] = null;
                    _airMap[i, j] = null;
                }
            }

        }

        public Map(GameEngine parent, int[,] initArray)
        {

            InitMapWithArray(parent, initArray);

        }

        
        #endregion

        #region Properties

        public int WidthInTiles
        {
            get
            {
                return _xTiles;
            }
        }

        public int HeightInTiles
        {
            get
            {
                return _yTiles;
            }
        }

        public Tile this[int x, int y]
        {
            get
            {
                return _tiles[x, y];
            }
            set
            {
                _tiles[x, y] = value;
            }
        }

        public Unit[,] GroundMap
        {
            get
            {
                return _groundMap;
            }
        }

        public Unit[,] AirMap
        {
            get
            {
                return _airMap;
            }
        }

        public GameEngine Parent
        {
            get
            {
                return _parent;
            }
        }

        #endregion

        #region Private Members

        /// <summary>
        /// Creates texture and tile objects
        /// </summary>

        protected GameEngine _parent;

        protected Texture[] _textures;
        protected bool[] groundPassability;
        protected bool[] airPassability;

        protected Tile[,] _tiles;
        protected Unit[,] _groundMap;
        protected Unit[,] _airMap;

        protected Sprite _sprTileSprite;

        protected int _xTiles;
        protected int _yTiles;
        protected int _scroll = 0;

        protected bool _editor;
        protected int _selectedTile;
        protected int _editOverX;
        protected int _editOverY;

        protected int _rbStartX = 0;
        protected int _rbStartY = 0;
        protected int _rbTilesX = 0;
        protected int _rbTilesY = 0;

        #endregion

        #region IRender Members
        /// <summary>
        /// Steps through the tile array and renders the tiles with texture
        /// </summary>
        public void Render()
        {

            foreach (Player p in Parent.Players)
                p.ResetFog();

            for (int j = _rbStartY; j < _rbStartY + _rbTilesY; ++j)
            {
                for (int i = _rbStartX; i < _rbStartX + _rbTilesX; ++i)
                {
                    if (Parent.LocalPlayer.FogArray[i, j] != FogStatus.Unexplorered)
                    {

                        if (Parent.LocalPlayer.FogArray[i, j] == FogStatus.Explorered)
                        {
                            _sprTileSprite.Alpha = 150;
                        }
                        else
                        {
                            _sprTileSprite.Alpha = 255;
                        }

                        _sprTileSprite.Left = (i * Globals.TILE_SIZE);
                        _sprTileSprite.Top = (j * Globals.TILE_SIZE);
                        _sprTileSprite.Render(_tiles[i, j].texTile);


                    }
                }
            }

        }

        public void Render(RenderLayer layer)
        {

            IRender[,] mapLayer = null;

            switch (layer)
            {
                case RenderLayer.MapTiles:
                    Render();
                    return;
                case RenderLayer.GroundUnits:
                    mapLayer = _groundMap;
                    break;
                case RenderLayer.AirUnits:
                    mapLayer = _airMap;
                    break;
                default:
                    throw new Exception("Map cannot render on layer: " + layer.ToString());
            }

            if (Parent.LocalPlayer.SelectedUnit != null)
            {
                if (layer == RenderLayer.GroundUnits && !(Parent.LocalPlayer.SelectedUnit is AirUnit))
                {
                    Parent.RenderSelectedUnitHighlighter();
                }
                else if (layer == RenderLayer.AirUnits && Parent.LocalPlayer.SelectedUnit is AirUnit)
                {
                    Parent.RenderSelectedUnitHighlighter();
                }
            }

            for (int j = _rbStartY; j < _rbStartY + _rbTilesY; ++j)
            {
                for (int i = _rbStartX; i < _rbStartX + _rbTilesX; ++i)
                {
                    if (mapLayer[i, j] != null)
                    {
                        if (Parent.LocalPlayer.FogArray[i, j] == FogStatus.Visible)
                        {
                            mapLayer[i, j].Render();
                        }
                    }
                }
            }

        }

        #endregion

        #region IInput Members

        public bool MouseDown(System.Windows.Forms.MouseButtons button, int x, int y)
        {

            if (_editor)
            {

                int xTile = x / Globals.TILE_SIZE;
                int yTile = y / Globals.TILE_SIZE;

                Console.WriteLine("xTile = " + xTile.ToString());
                Console.WriteLine("yTile = " + yTile.ToString());

                ((MapEditor)this).TileClicked(xTile, yTile, button);

            }

            return false;
        }

        public bool MouseUp(System.Windows.Forms.MouseButtons button, int x, int y)
        {
            return false;
        }

        public bool MouseMove(int x, int y)
        {

            _scroll = 0;

            if (x < 10)
                _scroll += (int)ScrollDirection.Left;

            if (y < 10)
                _scroll += (int)ScrollDirection.Up;

            if (x > Globals.iScreenWidth-10)
                _scroll += (int)ScrollDirection.Right;

            if (y > Globals.iScreenHeight - 10)
                _scroll += (int)ScrollDirection.Down;

            return false;

        }

        public bool KeyDown(System.Windows.Forms.Keys key, bool shift)
        {
            if (_editor)
            {

                if (key == System.Windows.Forms.Keys.S)
                {

                    using (StreamWriter sw = new StreamWriter(Globals.RESOURCE_DIRECTORY + @"Maps\" + Globals.iMapEditorMapName + ".map"))
                    {

                        for (int j = 0; j < _tiles.GetLength(1); ++j)
                        {

                            if (j != 0)
                                sw.WriteLine();

                            for (int i = 0; i < _tiles.GetLength(0); ++i)
                            {
                                if (i != 0)
                                    sw.Write("|");
                                sw.Write(_tiles[i, j].ID.ToString());

                            }


                        }

                    }
                }
            }

            return false;
        }

        public bool KeyUp(System.Windows.Forms.Keys key, bool shift)
        {
            return false;
        }

        #endregion

        #region IMotion Members

        public void Motion()
        {  

            if ((_scroll & (int)ScrollDirection.Up) > 0)
            {
                ScrollScreen(0, -8);
            }

            if ((_scroll & (int)ScrollDirection.Down) > 0)
            {
                ScrollScreen(0, 8);
            }

            if ((_scroll & (int)ScrollDirection.Left) > 0)
            {
                ScrollScreen(-8, 0);
            }

            if ((_scroll & (int)ScrollDirection.Right) > 0)
            {
                ScrollScreen(8, 0);
            }

        }

        #endregion

        #region Methods

        public void InitMapWithArray(GameEngine parent, int[,] initArray)
        {

            _parent = parent;

            LoadTextures();

            // Create map based on array

            _xTiles = initArray.GetUpperBound(1) + 1;
            _yTiles = initArray.GetUpperBound(0) + 1;

            _tiles = new Tile[_xTiles, _yTiles];
            _groundMap = new Unit[_xTiles, _yTiles];
            _airMap = new Unit[_xTiles, _yTiles];

            for (int j = 0; j < _yTiles; j++)
            {
                for (int i = 0; i < _xTiles; i++)
                {

                    int textureID = initArray[j, i];
                    _tiles[i, j] = new Tile(_textures[textureID],           // Reference to texture
                                            groundPassability[textureID],   // Is this texture ground passable?
                                            airPassability[textureID]);     // Is this texture air passable?
                    _tiles[i, j].ID = textureID;                            // Store texture id for later

                }
            }

        }

        public bool TileIsPassable(UnitLayer layer, int x, int y)
        {

            if (x < 0 || y < 0 || x > _tiles.GetUpperBound(0) || y > _tiles.GetUpperBound(1))
                return false;

            switch (layer)
            {
                case UnitLayer.Ground:
                    if (_tiles[x, y].GroundPassable && _groundMap[x, y] == null)
                        return true;
                    else
                        return false;
                case UnitLayer.Air:
                    if (_tiles[x, y].AirPassable && _airMap[x, y] == null)
                        return true;
                    else
                        return false;
                default:
                    throw new Exception("Error in Map.TileIsPassable, unknown layer.");
            }

        }

        public bool TileIsPassable(UnitLayer layer, MapPoint point)
        {

            return TileIsPassable(layer, point.X, point.Y);

        }

        protected void LoadTextures()
        {

            // Create the one sprite to render all Textures
            _sprTileSprite = new Sprite(0, 0, Globals.TILE_SIZE, Globals.TILE_SIZE);

            // Create an array of Texture filenames to initialize with
            string[] fileNames =
            {
                "Images/Grass.bmp",
                "Images/Water.bmp",
                "Images/Mountains.bmp"
            };

            groundPassability = new bool[]
            {
                true,   // Grass
                false,  // Water
                false   // Mountains
            };

            airPassability = new bool[]
            {
                true,   // Grass
                true,   // Water
                true    // Mountains
            };

            // Create the array of Textures using fileNames array
            _textures = new Texture[fileNames.Length];
            for (int i = 0; i < fileNames.Length; i++)
                _textures[i] = new Texture(fileNames[i]);

        }

        public void ScrollScreen(int x, int y)
        {

            int mapWidth = _xTiles * Globals.TILE_SIZE;
            int mapHeight = _yTiles * Globals.TILE_SIZE;


            Globals.iScreenOffsetX += x;
            Globals.iScreenOffsetY += y;


            if (Globals.iScreenOffsetX < -40)
                Globals.iScreenOffsetX = -40;

            if (Globals.iScreenOffsetY < -70)
                Globals.iScreenOffsetY = -70;

            if (Globals.iScreenOffsetX + Globals.iScreenWidth > mapWidth + 40)
                Globals.iScreenOffsetX = (mapWidth + 40) - Globals.iScreenWidth;

            if (Globals.iScreenOffsetY + Globals.iScreenHeight > mapHeight + 230)
                Globals.iScreenOffsetY = (mapHeight + 230) - Globals.iScreenHeight;


        }

        public void DetermineRenderBounds()
        {

            _rbStartX = Globals.iScreenOffsetX / 64;
            _rbStartY = Globals.iScreenOffsetY / 64;

            if (_rbStartX < 0) _rbStartX = 0;
            if (_rbStartY < 0) _rbStartY = 0;

            _rbTilesX  = (Globals.iScreenWidth / 64) + 1;
            _rbTilesY = (Globals.iScreenHeight / 64) + 1;

            if ((_rbStartX + _rbTilesX) > _tiles.GetUpperBound(0))
                _rbTilesX = _tiles.GetUpperBound(0) - _rbStartX + 1;

            if ((_rbStartY + _rbTilesY) > _tiles.GetUpperBound(1))
                _rbTilesY = _tiles.GetUpperBound(1) - _rbStartY + 1;

        }

        public bool IsWithinRenderBounds(MapPoint point)
        {

            if (point.X >= _rbStartX &&
                point.Y >= _rbStartY &&
                point.X <= _rbStartX + _rbTilesX &&
                point.Y <= _rbStartY + _rbTilesY)
            {
                return true;
            }
            else
            {
                return false;
            }

        }

        #endregion

    }
}
